import { isNil } from 'lodash-es'
import type { NavigationGuardNext, RouteMeta } from 'vue-router'
import { appConfig } from '../config/app';
import { HttpRequestConfig, isShowErrorDialog } from '../http'
import { http, isHashRouter, login, serverRbacCheck } from '../index'
import { Dialog } from '../modal'
import { StorageExpireError } from '../storage'
import { appendUrlHash, appendUrlSearch } from '../util'
import { getTokenId, RefreshTokenError, RefreshTokenExpireError, TokenNotExistError } from './index'


export interface LoginConfig {
  afterLoginCheck: (meta: RouteMeta, next: NavigationGuardNext) => Promise<boolean>
  afterLogout: (options?: any) => Promise<any>
  afterToken: (accessTokenPayload: object, refreshTokenPayload: object, idTokenPayload: object) => Promise<any> | void
  beforeLoginCheck: () => Promise<any>
  beforeLogout: (options?: any) => Promise<any> | void
  getServerRefreshToken: (refreshToken: string | null, config: HttpRequestConfig) => Promise<any>
  isLogin: () => boolean
  loginUrl: string
  tokenCheckError: (e: Error) => Promise<any>
  tokenIdName: string
  tokenIdField: string
  tokenName: string
  idTokenName: string
  refreshTokenName: string
  tokenExpOffset: number
}

export const loginConfig: LoginConfig = {
  afterLoginCheck: async (meta: RouteMeta, next: NavigationGuardNext): Promise<boolean> => serverRbacCheck(meta, next),
  afterLogout: async (options: any): Promise<void> => {
    let url = encodeURIComponent(options?.url ? options.url : location.href)
    if (isHashRouter()) {
      url = appendUrlHash('redirect_uri', url, loginConfig.loginUrl)
      location.replace(url)
      location.reload()
    } else {
      location.replace(appendUrlSearch('redirect_uri', url, loginConfig.loginUrl))
    }
  },
  afterToken: (accessTokenPayload: object, refreshTokenPayload: object, idTokenPayload: object): void => {
    appConfig.env === 'dev' && console.info('accessTokenPayload', accessTokenPayload, 'refreshTokenPayload', refreshTokenPayload, 'idTokenPayload', idTokenPayload)
  },
  beforeLoginCheck: async (): Promise<void> => {
    await loginConfig.afterLogout()
  },
  beforeLogout: (options?: any) => {
    http.postForm('/oauth/logout', undefined, { appKey: 'oauth' }).catch(console.error)
  },
  getServerRefreshToken: (refresh_token: string | null, config: HttpRequestConfig) => {
    config.appKey = 'oauth'
    return http.postForm('/oauth/token', { grant_type: 'refresh_token', refresh_token }, config)
  },
  isLogin: () => !isNil(getTokenId()),
  tokenCheckError: async (e: Error): Promise<void> => {
    if (e instanceof StorageExpireError || e instanceof RefreshTokenExpireError || e instanceof RefreshTokenError) {
      if (!isShowErrorDialog.value) {
        try {
          isShowErrorDialog.value = true
          await Dialog.confirm({ content: e.message ?? '登录信息已过期，请重新登录！', title: '登录信息已过期', okText: '去登录' })
          await login.logout({ reload: true })
        } finally {
          isShowErrorDialog.value = false
        }
      }
    } else if (e instanceof TokenNotExistError) {
      await login.logout({ reload: true })
    } else if (e instanceof RefreshTokenError) {
      await login.logout({ reload: true })
    } else {
      throw e
    }
  },
  loginUrl: window.dmyConfig?.login?.loginUrl || '',
  tokenIdField: window.dmyConfig?.login?.tokenIdField || 'id',
  tokenIdName: window.dmyConfig?.login?.tokenIdName || '$id$',
  tokenName: window.dmyConfig?.login?.tokenName || '$tk$',
  refreshTokenName: window.dmyConfig?.login?.refreshTokenName || '$rtk$',
  idTokenName: window.dmyConfig?.login?.idTokenName || '$itk$',
  tokenExpOffset: window.dmyConfig?.login?.tokenExpOffset || 5 * 60,
}
